﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;

namespace WindowsWorkflow.SequentialActivities
{
	public partial class Navigator: CompositeActivity
	{
		#region props
		public static DependencyProperty StartWithProperty = 
			DependencyProperty.Register("StartWith", typeof(string), typeof(Navigator),
			new PropertyMetadata(DependencyPropertyOptions.Metadata));

		public string StartWith
		{
			get
			{
				return ((string)(base.GetValue(Navigator.StartWithProperty)));
			}
			set
			{
				base.SetValue(Navigator.StartWithProperty, value);
			}
		}

		public static DependencyProperty NavigateToProperty =
					DependencyProperty.RegisterAttached("NavigateTo", typeof(string), 
					typeof(Navigator));

		public static object GetNavigateTo(object dependencyObject)
		{
			DependencyObject o = dependencyObject as DependencyObject;
			return o.GetValue(Navigator.NavigateToProperty);
		}

		public static void SetNavigateTo(object dependencyObject, object value)
		{
			DependencyObject o = dependencyObject as DependencyObject;
			o.SetValue(Navigator.NavigateToProperty, value);
		}

		public static DependencyProperty NavigatingEvent =
			DependencyProperty.Register(
				"Navigating", typeof (EventHandler<NavigatorEventArgs>), typeof (Navigator));

		public event EventHandler<NavigatorEventArgs> Navigating
		{
			add
			{
				base.AddHandler(Navigator.NavigatingEvent, value);
			}
			remove
			{
				base.RemoveHandler(Navigator.NavigatingEvent, value);
			}
		}
		#endregion

		public Navigator()
		{
			InitializeComponent();
		}

		protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
		{
			if(this.TryNavigatingTo(executionContext, this.StartWith))
				return ActivityExecutionStatus.Executing;

			return ActivityExecutionStatus.Closed;
		}

		private bool TryNavigatingTo(ActivityExecutionContext context, string nextActivityName)
		{
			ActivityExecutionContextManager manager = context.ExecutionContextManager;
			// populate history
			List<Activity> history=new List<Activity>();
			foreach(Guid ctxId in manager.PersistedExecutionContexts)
			{
				ActivityExecutionContext serializedContext = manager.GetPersistedExecutionContext(ctxId);
				history.Add(serializedContext.Activity);

				// getpersistedexecutioncontext above removed the context 
				// so we need to explicitly add it back 
				context.ExecutionContextManager.CompleteExecutionContext(serializedContext, true);
			}

			// raise the event 
			NavigatorEventArgs args = new NavigatorEventArgs(history.AsReadOnly());
			RaiseGenericEvent(Navigator.NavigatingEvent, this, args);

			Activity nextActivity = null;
			if (args.NavigateTo != null)
				nextActivity = args.NavigateTo;
			else if (!string.IsNullOrEmpty(nextActivityName))
				nextActivity = this.GetActivityByName(nextActivityName);
			if(nextActivity !=null)
			{
				ActivityExecutionContext innerContext = manager.CreateExecutionContext(nextActivity);
				innerContext.Activity.Closed += new EventHandler<ActivityExecutionStatusChangedEventArgs>(Activity_Closed);
				innerContext.ExecuteActivity(innerContext.Activity);
				return true;
			}

			return false;
		}

		void Activity_Closed(object sender, ActivityExecutionStatusChangedEventArgs e)
		{
			ActivityExecutionContext context = sender as ActivityExecutionContext;
			ActivityExecutionContextManager manager = context.ExecutionContextManager;
			ActivityExecutionContext innerContext = manager.GetExecutionContext(e.Activity);
			// first, unscribe to the inner context's activity
			innerContext.Activity.Closed -= Activity_Closed;
			// remove the inner context and serialize it
			manager.CompleteExecutionContext(innerContext,true);

			string nextActivityName = Navigator.GetNavigateTo(innerContext.Activity) as string;
			if(!this.TryNavigatingTo(context,nextActivityName))
				context.CloseActivity();
		}
	}
}
